package com.tools.web.email;

import com.tools.common.exception.MailOperationException;
import com.tools.common.object.DateKit;
import com.tools.common.object.Note;
import com.tools.web.http.UploadFile;
import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Header;
import javax.mail.Message;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;

import java.util.*;

/**
 * 自定义的邮件信息包装实体
 * */
@Note("自定义的邮件信息包装实体")
public final class MailEntity {

    @Note("邮件编号")
    private final int number;

    @Note("邮件主题")
    private final String subject;

    @Note("邮件内容的 MIME 类型")
    private final String contentType;

    @Note("发件人名称")
    private final String fromName;

    @Note("发件人的邮箱账户")
    private final String fromEmail;

    @Note("发件时间")
    private final Date sendDate;

    @Note("邮件内容中是否有内嵌别的内容，有的话其 ID 在这")
    private final String contentID;

    @Note("邮件的文本内容存储在此")
    private String textContent;

    @Note("邮件的 HTML 内容存储在此")
    private String htmlContent;

    @Note("邮件的附件存储在此")
    private List<UploadFile> attachments;

    @Note("是否保存邮件的所有 header 内容")
    private final boolean saveHeaders;

    @Note("如果 saveHeaders 为 true，则保存所有 header 在这")
    private Map<String, String> headers;

    @Note("入参的 Message 源对象暂时保存在这，主要还是为了用于回复邮件时能返回实例")
    private final MimeMessage sourceMessage;

    /* **********************************************************************************
     *
     *          构造器
     *
     * *********************************************************************************
     * */

    public MailEntity(Message msg) {
        this(msg, false);
    }

    public MailEntity(Message msg, boolean saveHeaders) {
        if(msg == null) throw new NullPointerException("不能处理 null 的邮件");
        MimeMessage message = (MimeMessage) msg;
        this.sourceMessage = message;
        this.saveHeaders = saveHeaders;
        try {
            this.number = msg.getMessageNumber();
            this.subject = MimeUtility.decodeText(message.getSubject());
            Address sender = message.getSender();
            if(sender == null) throw new MailOperationException("该邮件没有发送者");
            String s = MimeUtility.decodeText(sender.toString());
            String[] arr = s.replace("\" ", "")
                    .replace(" \"", "")
                    .split(" ");
            if(arr.length < 2) throw new MailOperationException("邮箱没有发送者");
            this.fromName = arr[0];
            String a1 = arr[1];
            this.fromEmail = a1.substring(a1.indexOf('<') + 1, a1.indexOf('>'));
            this.sendDate = message.getSentDate();
            this.contentID = message.getContentID();
            this.contentType = message.getContentType();
            if(saveHeaders) {
                private_handleHeaders(message);
            }
            private_handleContentBody(message);
        } catch (Exception e) {
            throw new MailOperationException(e);
        }
    }

    public static MailEntity of(Message msg) {
        return new MailEntity(msg);
    }

    public static MailEntity of(Message msg, boolean saveHeaders) {
        return new MailEntity(msg, saveHeaders);
    }

    /* **********************************************************************************
     *
     *          Getter
     *
     * *********************************************************************************
     * */

    public int getNumber() {
        return number;
    }

    public String getSubject() {
        return subject;
    }

    public String getContentType() {
        return contentType;
    }

    public String getFromName() {
        return fromName;
    }

    public String getFromEmail() {
        return fromEmail;
    }

    public String getContentID() {
        return contentID;
    }

    public Date getSendDate() {
        return sendDate;
    }

    public String getSendDateString() {
        if(sendDate == null) return "";
        return DateKit.format(sendDate);
    }

    public long getSendDateTimestamp() {
        if(sendDate == null) return -1;
        return sendDate.getTime();
    }

    public String getTextContent() {
        return textContent;
    }

    public String getHtmlContent() {
        return htmlContent;
    }

    public List<UploadFile> getAttachments() {
        return attachments;
    }

    public boolean isSaveHeaders() {
        return saveHeaders;
    }

    public Map<String, String> getHeaders() {
        return headers;
    }

    public MimeMessage getSourceMessage() {
        return sourceMessage;
    }

    public MimeMessage reply(boolean replyToAll) {
        return (MimeMessage) MailKit.replyMessage(sourceMessage, replyToAll);
    }


    @Override
    public String toString() {
        return "MailEntity{" +
                "number=" + number +
                ", fromName='" + fromName + '\'' +
                ", fromEmail='" + fromEmail + '\'' +
                ", sendDate=" + getSendDateString() +
                ", subject='" + subject + '\'' +
                ", contentID='" + contentID + '\'' +
                ", contentType='" + contentType + '\'' +
                ", textContent='" + textContent + '\'' +
                "\n, attachments=" + attachments +
                ", saveHeaders=" + saveHeaders +
                "\n, htmlContent=" + htmlContent +
                "\n, headers=" + (saveHeaders ? ("\n\n" + headers) : null) +
                "\n}";
    }

    /* **********************************************************************************
     *
     *          私有逻辑
     *
     * *********************************************************************************
     * */

    @Note("初始化附件列表")
    private void private_initAttachmentList() {
        if(attachments != null) return;
        this.attachments = new ArrayList<>();
    }

    @Note("将邮件的所有 header 保存")
    private void private_handleHeaders(Message message) throws Exception {
        Enumeration<Header> headers = message.getAllHeaders();
        if(!headers.hasMoreElements()) return;
        this.headers = new HashMap<>();
        while (headers.hasMoreElements()) {
            Header header = headers.nextElement();
            if(header != null) {
                this.headers.put(header.getName(), header.getValue());
            }
        }
    }

    @Note("根据 contentType 处理邮件的内容")
    private void private_handleContentBody(MimeMessage message) throws Exception {
        Object content = message.getContent();
        if(message.isMimeType("text/html")) {
            this.htmlContent = content.toString();
            return;
        }
        if(message.isMimeType("text/plain")) {
            this.textContent = content.toString();
            return;
        }
        if(message.isMimeType("multipart/*")) {
            private_handleMimeMultipart((MimeMultipart) content);
        }
    }

    @Note("当邮件的内容的 contentType 为 multipart 时，需要做进一步的分类处理")
    private void private_handleMimeMultipart(MimeMultipart multipart) throws Exception {
        int count = multipart.getCount();
        StringBuilder textBuilder = new StringBuilder()
                .append(textContent == null ? "" : textContent);
        StringBuilder htmlBuilder = new StringBuilder()
                .append(htmlContent == null ? "" : htmlContent);
        for (int i = 0; i < count; i++) {
            BodyPart bodyPart = multipart.getBodyPart(i);
            Object bodyContent = bodyPart.getContent();
            if(bodyContent == null) {
                continue;
            }
            if(bodyPart.isMimeType("text/html")) {
                htmlBuilder.append(bodyContent);
                continue;
            }
            if(bodyPart.isMimeType("text/plain")) {
                textBuilder.append(bodyContent);
                continue;
            }
            if(bodyPart.isMimeType("application/octet-stream")) {
                String disposition = bodyPart.getDisposition();
                if(BodyPart.ATTACHMENT.equalsIgnoreCase(disposition) || BodyPart.INLINE.equalsIgnoreCase(disposition)) {
                    private_initAttachmentList();
                    String filename = bodyPart.getFileName();
                    this.attachments.add(new UploadFile(filename, filename, bodyPart.getInputStream()));
                }
                continue;
            }
            if(bodyContent instanceof MimeMultipart) {
                private_handleMimeMultipart((MimeMultipart) bodyContent);
                continue;
            }
            throw new MailOperationException("不知道 【" + bodyContent + "】 是什么类型的内容");
        }
        this.textContent = textBuilder.toString();
        this.htmlContent = htmlBuilder.toString();
    }

}
